#ifndef __M_MSGQUEUE_H__
#define __M_MSGQUEUE_H__
#include"../mqcommon/helper.hpp"
#include"../mqcommon/logger.hpp"
#include<unordered_map>
#include<memory>
#include<mutex>
#include<sstream>
#include<assert.h>

namespace mq{
    struct MsgQueue{
        using ptr = std::shared_ptr<MsgQueue>;
        std::string name;
        bool durable;
        bool exclusive;
        bool auto_delete;
        google::protobuf::Map<std::string,std::string> args;
        MsgQueue(){}
        MsgQueue(const std::string& qname,bool qdur,bool qexcl,bool qdele,
            const google::protobuf::Map<std::string,std::string>& qargs):
            name(qname),durable(qdur),exclusive(qexcl),auto_delete(qdele),args(qargs){}

        void setargs(const std::string& str_args)
        {
            const std::string sep1 = "=";
            const std::string sep2 = "&";
            std::vector<std::string> vcs;
            Stringsplit::split(str_args,&vcs,sep2);
            for(auto& str:vcs){
                size_t pos = str.find(sep1);
                std::string key = str.substr(0,pos);
                std::string val = str.substr(pos+sep1.size());
                args.insert({key,val});
            }
        }

        std::string getargsstr(){
            std::string str_args;
            if(!args.empty()){
                for(auto e:args){
                    str_args += e.first + "=" + e.second + "&";
                }
            }
            return str_args;
        }
    };
    using MsgqueueMap = std::unordered_map<std::string,MsgQueue::ptr>;
    class MsgQueueSqlMaper{
        public:
            MsgQueueSqlMaper(const std::string& filename):_sql_hander(filename){
                std::string path = Filehelper::parentDirectory(filename);
                Filehelper::createDirectory(path);
                assert(_sql_hander.open());
                createTable();
            }
            void createTable(){
            // std::string name;
            // bool durable;
            // bool exclusive;
            // bool auto_delete;
            // std::unordered_map<std::string,std::string> args;
                //create table if no exists queue_table(name varchar(32) primary key, durable int, exclusive int,
                //auto_delete int, args varchar(64));
                std::stringstream ss;
                ss<<"create table if not exists queue_table(";
                ss<<"name varchar(32) primary key, durable int,";
                ss<< " exclusive int, auto_delete int, args varchar(64));";
                int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
                if(ret == false){
                    LOG_ERRORM("cteate table queue false...");
                    abort();
                }
            }
            void deleteTable(){
                //drop table if exists queue_table;
                std::stringstream ss;
                ss<<"drop table if exists queue_table;";
                int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
                if(ret == false){
                    LOG_ERRORM("drop table queue false...");
                    abort();
                }
            }
            void insert(MsgQueue::ptr& msgq){
                //insert into queue_table values('%s',%d,%d,%d,'%s');
                std::stringstream ss;
                ss<<"insert into queue_table values(";
                ss<<"'"<<msgq->name<<"',";
                ss<<msgq->durable<<",";
                ss<<msgq->exclusive<<",";
                ss<<msgq->auto_delete<<",";
                ss<<"'"<<msgq->getargsstr()<<"');";

                int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
                //testing
                if(ret == false){
                    LOG_ERRORM("insert queue false...");
                }
            }
            void remove(MsgQueue::ptr& msgq){
                //delete from queue_table where name = "%s";
                std::stringstream ss;   
                ss<<"delete from queue_table where name = '"<<msgq->name<<"';";
                //testing
                int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
                if(ret == false){
                    LOG_ERRORM("remove queue false...");
                }
            }
            MsgqueueMap recovery(){
                //select name, durable, exclusive, auto_delete, args from queue_table;
                std::stringstream ss;   
                ss<<"select name, durable, exclusive, auto_delete, args from queue_table;";
                MsgqueueMap queuemap;
                int ret = _sql_hander.exec(ss.str(),MsgQueueCallback,&queuemap);
                if(ret == false){
                    LOG_ERRORM("recovery queue false...");
                }
                return queuemap;
            }
        private:
              static int MsgQueueCallback(void* arg,int col,char** row,char** field_name){
                MsgqueueMap* quemap = static_cast<MsgqueueMap*>(arg);
                MsgQueue::ptr queptr = std::make_shared<MsgQueue>();
                queptr->name = row[0];
                queptr->durable = std::stoi(row[1]);
                queptr->exclusive = std::stoi(row[2]);
                queptr->auto_delete = std::stoi(row[3]);
                if(row[4]) queptr->setargs(row[4]);
                quemap->insert(std::make_pair(queptr->name,queptr));
                return 0;
              }
        private:
            SqliteHelper _sql_hander;
    };

    class MsgQueueManager{
        public:
        using ptr = std::shared_ptr<MsgQueueManager>;
        MsgQueueManager(const std::string& filename):_msgqueuemaper(filename){
            _msgqueues = _msgqueuemaper.recovery();
            LOG_DEBUGM("queue init...");
        }
        bool declare(const std::string& iname,bool idur,bool iexcl,bool idele,const google::protobuf::Map<std::string,std::string>& iargs){
            std::unique_lock<std::mutex> lock;
            auto it = _msgqueues.find(iname);
            if(it != _msgqueues.end()) 
                return true;
            MsgQueue::ptr msque = std::make_shared<MsgQueue>(iname,idur,iexcl,idele,iargs);
            if(idur == true) _msgqueuemaper.insert(msque);
            
            _msgqueues.insert(std::make_pair(iname,msque));
            return true;            
        }
        void deleteMsgQueue(const std::string& name){
            std::unique_lock<std::mutex> lock;
            auto itm = _msgqueues.find(name);
            if(itm == _msgqueues.end()){
                return;
            }
            if(itm->second->durable) _msgqueuemaper.remove(itm->second);
            _msgqueues.erase(name);
        }
        bool exists(const std::string& name){
            std::unique_lock<std::mutex> lock;
            auto it = _msgqueues.find(name);
            if(it == _msgqueues.end()){
                return false;
            }
            return true;
        }

        MsgQueue::ptr select(const std::string& name){
            std::unique_lock<std::mutex> lock;
            auto itm = _msgqueues.find(name);
            if(itm == _msgqueues.end()){
                return MsgQueue::ptr();
            }
            return itm->second;
        }

        MsgqueueMap allQueue(){
            std::unique_lock<std::mutex> lock;
            return _msgqueues;
        }

        size_t size(){
            std::unique_lock<std::mutex> lock;
            return _msgqueues.size();
        }

        void clear(){
            std::unique_lock<std::mutex> lock;
            _msgqueuemaper.deleteTable();
            _msgqueues.clear();
        }

        private:
        std::mutex _mutex;
        MsgQueueSqlMaper _msgqueuemaper;
        std::unordered_map<std::string,MsgQueue::ptr> _msgqueues;
    };
}

#endif